package com.nw.dsl4j.runtime.parser;

import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

import org.antlr.runtime.Lexer;
import org.antlr.runtime.Parser;

import com.nw.dsl4j.generator.Generator;
/**
 * A java 6 implemetation that can compile the dsl in runtime.
 * @author eli
 *
 * @param <P>
 */
public class Java6RuntimeCompilerDsl4jParserImpl<P> extends AbstractReflectionDsl4jParserImpl<P> {

	private static String target = ".dslj/tmp/";

	@SuppressWarnings("unchecked")
	protected Java6RuntimeCompilerDsl4jParserImpl(ClassLoader cl, Class<?> c) {
		this(cl, c, c);
	}

	public Java6RuntimeCompilerDsl4jParserImpl(ClassLoader cl, Class<?> dslClass, Class<?> classToParse) {
		this(cl, dslClass, classToParse, null);
	}

	@SuppressWarnings("unchecked")
	public Java6RuntimeCompilerDsl4jParserImpl(ClassLoader cl, Class<?> dslClass, Class<?> classToParse,
			Dsl4jParserContext parserContext) {
		super(dslClass, classToParse, parserContext);
		try {
			URLClassLoader compiledClassLoader = generateAndCompileAndLoad(cl, dslClass);

			lexerClazz = (Class<? extends Lexer>) compiledClassLoader.loadClass(getLexerClassName());
			parserClazz = (Class<? extends Parser>) compiledClassLoader.loadClass(getParserClassName());
		} catch (Exception e) {
			throw new RuntimeException("Cannot instantiate lexer/parser classes" + e.getMessage(), e);
		}
	}

	private URLClassLoader generateAndCompileAndLoad(ClassLoader cl, Class<?> c) throws Exception,
			ClassNotFoundException, IOException, MalformedURLException {
		Generator generator = new Generator(c.getCanonicalName(), target + "/java/" , target + "/grammar/", cl);

		generator.generate();

		compile();

		URLClassLoader compiledClassLoader = new URLClassLoader(new URL[] { new URL("file:" + target + "/java/") }, cl);
		return compiledClassLoader;
	}

	public void compile() {
		JavaCompiler c = ToolProvider.getSystemJavaCompiler();
		String className = getDslClass().getSimpleName();
		String prefix = getDslClass().getPackage().getName() + ".parser." + className;
		prefix = prefix.replace(".", "/");
		String lexer = prefix + "Lexer.java";
		String parser = prefix + "Parser.java";

		StandardJavaFileManager fm = c.getStandardFileManager(null, null, null);

		Iterable<? extends JavaFileObject> fs = 
			fm.getJavaFileObjects(
				new File(target + "/java", lexer), 
				new File(target + "/java", parser));

		Boolean noErrors = c.getTask(null, fm, null, null, null, fs).call();
		if (!noErrors) {
			throw new IllegalStateException("Could not compile grammer. See error in stdout");
		}
	}
}
